package com.cy.servlet;

import com.alibaba.druid.support.json.JSONUtils;
import com.cy.anno.Component;
import com.cy.anno.Controller;
import com.cy.anno.RequestMapping;
import com.cy.factory.BeanFactory;
import com.cy.interceptor.HandleInterceptor;
import com.cy.pojo.Handler;
import com.cy.utils.ClassUtils;
import com.cy.utils.Scanner;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Author ChenYan
 * @Date 2021/7/1 13:38
 * @Description
 */
@Component
public class CyDispatchServlet extends HttpServlet {

    Properties properties = new Properties();

    List<String> classNames = new ArrayList<>();

    List<Handler> handlerMapping = new ArrayList<>();

    Map<String, Object> ioc = new HashMap<>();

    @Override
    public void init(ServletConfig config) throws ServletException {

        try {
            //1.加载springmvc.properties的配置文件
            doLoadProperties(config);

            //2.扫描配置包的所有文件
            doScanPackage();

            //3.初始化HandlerMapping，建立请求和Handler方法的映射关系
            initHandlerMapping();

            System.out.println("======SpringMVC 初始化完成======");
        }
        catch (Exception e){
            e.printStackTrace();
            System.out.println("======SpringMVC 初始化失败======");
        }

    }

    private void initHandlerMapping() {
        for (Map.Entry<String, Object> entry : ioc.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();

            //如果是Controller
            if (aClass.isAnnotationPresent(Controller.class)) {
                String basePath = "";
                if (aClass.isAnnotationPresent(RequestMapping.class)){
                    //如果类名上设置了RequestMapping，那么基础目录上加上路径，/base
                    RequestMapping requestMapping = aClass.getDeclaredAnnotation(RequestMapping.class);
                    basePath = requestMapping.value();
                }

                Method[] methods = aClass.getMethods();

                for (Method method : methods) {
                    //如果方法上面写了RequestMapping注解
                    if (method.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping requestMapping = method.getDeclaredAnnotation(RequestMapping.class);
                        String requestPath = requestMapping.value();
                        String pattern = basePath + requestPath;
                        //初始化handler
                        Handler handler = new Handler(entry.getValue(), method, Pattern.compile(pattern));

                        Parameter[] parameters = method.getParameters();
                        for (int i = 0; i < parameters.length; i++) {
                            //拿到所有参数，放入ParamIndexMapping中
                            String name = parameters[i].getName();
                            handler.getParamIndexMapping().put(name, i);
                        }
                        //添加到handlerMapping
                        handlerMapping.add(handler);
                    }
                }
            }

        }
    }

    private void doLoadProperties(ServletConfig config) throws IOException {
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        System.out.println(contextConfigLocation);
        InputStream inputStream = CyDispatchServlet.class.getClassLoader().getResourceAsStream(contextConfigLocation);
        properties.load(inputStream);
    }

    private void doScanPackage() throws Exception {
        String scanPackage = properties.getProperty("scanPackage");
        System.out.println("mvc扫描路径="+scanPackage);
        //借助BeanFactory初始化Controller的Bean
        BeanFactory beanFactory = BeanFactory.getInstance(scanPackage);
        ioc = beanFactory.getSingletonObject();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        System.out.println("======CyDispatchServlet 有新的请求====== uri=" + req.getRequestURI());

        Handler handler = getHandler(req);
        if (handler == null){
            System.out.println(req.getRequestURI() + "没有找到对应的Handler");
            resp.getWriter().write("404 not found");
            return;
        }

        Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();

        Object[] paraValues = new Object[parameterTypes.length];

        Map<String, String[]> parameterMap = req.getParameterMap();

        for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
            String name = entry.getKey();
            // 处理同一个名字多个变量的问题，如同 1,2
            String value = StringUtils.join(entry.getValue(), ",");

            Integer index = handler.getParamIndexMapping().get(name);
            if (index == null){
                continue;
            }
            else {
                paraValues[index] = value;
            }
        }

        //对req和resp做特殊处理
        Integer requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
        if (requestIndex != null){
            paraValues[requestIndex] = req;
        }
        Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
        if (responseIndex != null){
            paraValues[responseIndex] = resp;
        }

        try {

            //Interceptor预处理
            boolean result = interceptorPreHandler(req, resp, handler);
            if (!result){
                System.out.println("被拦截，不准往下执行");
                return;
            }

            System.out.println("======开始执行具体方法===="+req.getRequestURI());
            //反射调用对应方法
            Object object = handler.getMethod().invoke(handler.getController(), paraValues);
            String jsonResult = JSONUtils.toJSONString(object);
            resp.setStatus(200);
            resp.getWriter().println(jsonResult);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 拦截器前处理
     * @param req
     * @param resp
     * @param handler
     */
    private boolean interceptorPreHandler(HttpServletRequest req, HttpServletResponse resp, Handler handler) throws Exception {
        List<Class> allInterceptorClass = ClassUtils.getAllClassByInterface(HandleInterceptor.class);

        for (Class interceptorClass : allInterceptorClass) {
            HandleInterceptor interceptor = (HandleInterceptor) interceptorClass.newInstance();
            boolean result = interceptor.preHandle(req, resp, handler);
            //如果返回的是false，则直接返回
            if (!result){
                return false;
            }
        }
        return true;
    }

    /**
     * 获取到请求对应的Handler
     * @param req
     * @return
     */
    private Handler getHandler(HttpServletRequest req) {
        String requestUri = req.getRequestURI();
        if (StringUtils.isBlank(requestUri)){
            return null;
        }
        for (Handler handler : handlerMapping) {
            //如果能匹配上某个Handler
            if (handler.getPattern().matcher(requestUri).find()) {
                return handler;
            }
        }
        return null;
    }
}
